// CryptUtility.cpp: implementation of the CryptUtility class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <assert.h>
#include "CryptUtility.h"

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif  // _WIN32_WINNT

#include <wincrypt.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CryptUtility::CryptUtility()
{
}

CryptUtility::~CryptUtility()
{
}

bool CryptUtility::Encrypt(const BYTE * pPlaintext, BYTE * pCryptotext, DWORD dwPlaintextLen, const BYTE * pPassword, DWORD dwPasswordLen)
{
	bool bRet = false;

	HCRYPTPROV hProv = NULL;
	HCRYPTHASH hHash = NULL;
	HCRYPTKEY hKey = NULL;

	if (pPassword == NULL)
	{
		//TRACE0("Password should not be empty!\n");
	}

	// Get handle to the default provider;
	else if (!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, 0) &&
		!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET) &&
		!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET) &&
		!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
	{
		//TRACE1("Error %X during CryptAcquireContext!\n", GetLastError());
	}

	// Encrypt the data with a session key derived from a password.
	// Create a hash object.
	else if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
	{
		//TRACE1("Error %x during CryptCreateHash!\n", GetLastError());
	}

	// Hash in the password data.
	else if (!CryptHashData(hHash, pPassword, dwPasswordLen, 0))
	{
		//TRACE1("Error %x during CryptHashData!\n", GetLastError());
	}

	// Derive a session key from the hash object.
	else if (!CryptDeriveKey(hProv, CALG_RC2, hHash, 0, &hKey))
	{
		//TRACE1("Error %x during CryptDeriveKey!\n", GetLastError());
	}

	else
	{
		memcpy(pCryptotext, pPlaintext, dwPlaintextLen);

		// Encrypt data
		DWORD dwEncryptedTextLen = dwPlaintextLen;
		if (!CryptEncrypt(hKey, 0, true, 0, pCryptotext, &dwEncryptedTextLen, dwPlaintextLen+8))
		{
			//TRACE1("Bytes required:%d\n", dwEncryptedTextLen);
			//TRACE1("Error %x during CryptEncrypt!\n", GetLastError());
			memset(pCryptotext, 0, dwPlaintextLen);
		}

		else
		{
			bRet = true;

#ifdef _DEBUG
			DWORD dwLen = dwPlaintextLen+8;
			BYTE * pBuffer = new BYTE[dwLen];

			memcpy(pBuffer, pCryptotext, dwLen);

			// Decrypt data
			if (!CryptDecrypt(hKey, 0, true, 0, pBuffer, &dwLen))
			{
				//TRACE1("Error %x during CryptDecrypt!\n", GetLastError());
			}
			else
			{
				assert(memcmp(pBuffer, (BYTE *)pPlaintext, dwPlaintextLen) == 0);
			}

			delete pBuffer;
#endif
		}
	}

	// Destroy session key.
	if (hKey != NULL) CryptDestroyKey(hKey);

	// Destroy hash object.
	if (hHash != NULL) CryptDestroyHash(hHash);

	// Release provider handle.
	if (hProv != NULL) CryptReleaseContext(hProv, 0);

	return bRet;
}

bool CryptUtility::Decrypt(const BYTE * pCryptotext, BYTE * pPlaintext, DWORD dwCryptotextLen, const BYTE * pPassword, DWORD dwPasswordLen)
{
	bool bRet = false;

	HCRYPTPROV hProv = NULL;
	HCRYPTHASH hHash = NULL;
	HCRYPTKEY hKey = NULL;

	if (pPassword == NULL)
	{
		//TRACE0("Password should not be empty!\n");
	}

	// Get handle to the default provider;
	else if (!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, 0) &&
		!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET) &&
		!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET) &&
		!CryptAcquireContext(&hProv, "Container", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
	{
		//TRACE1("Error %X during CryptAcquireContext!\n", GetLastError());
	}

	// Encrypt the data with a session key derived from a password.
	// Create a hash object.
	else if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
	{
		//TRACE1("Error %x during CryptCreateHash!\n", GetLastError());
	}

	// Hash in the password data.
	else if (!CryptHashData(hHash, pPassword, dwPasswordLen, 0))
	{
		//TRACE1("Error %x during CryptHashData!\n", GetLastError());
	}

	// Derive a session key from the hash object.
	else if (!CryptDeriveKey(hProv, CALG_RC2, hHash, 0, &hKey))
	{
		//TRACE1("Error %x during CryptDeriveKey!\n", GetLastError());
	}

	else
	{
		memcpy(pPlaintext, pCryptotext, dwCryptotextLen);

		// Encrypt data
		if (!CryptDecrypt(hKey, 0, true, 0, pPlaintext, &dwCryptotextLen))
		{
			//TRACE1("Error %x during CryptDecrypt!\n", GetLastError());
			memset(pPlaintext, 0, dwCryptotextLen);
		}
		else
		{
			bRet = true;
		}
	}

	// Destroy session key.
	if (hKey != NULL) CryptDestroyKey(hKey);

	// Destroy hash object.
	if (hHash != NULL) CryptDestroyHash(hHash);

	// Release provider handle.
	if (hProv != NULL) CryptReleaseContext(hProv, 0);

	return bRet;
}

std::string CryptUtility::ByteArrayToString(BYTE * lpBuf, int nBufSize)
{
	std::string str;
	for (int i = 0;i < nBufSize;i++)
	{
		BYTE by = lpBuf[i];
		BYTE a = by/16;
		char ch;
		if (a < 10) ch = '0'+a;
		else ch = 'A'+a-10;
		str += ch;
		a = by%16;
		if (a < 10) ch = '0'+a;
		else ch = 'A'+a-10;
		str += ch;
	}
	return str;
}